package ws.performancetest.tests.synchronization.task;

import java.util.concurrent.ThreadPoolExecutor;
import java.util.logging.Level;
import java.util.logging.Logger;
import ws.performancetest.tests.synchronization.db.DBTestResult;
import ws.performancetest.tests.synchronization.db.IDBRegister;
import ws.performancetest.tests.synchronization.lock.SharedList;

/**
 * Task abstraction for an operation to be executed on some shared memory.
 * For each task one {@link ThreadPoolExecutor} will create one thread
 * (or assign one available) to run this task.
 *
 * @see ThreadPoolExecutor
 * @see TaskCreator
 * 
 * @author Wilson Santos, 36228 <wilson.santos@ua.pt>
 */
public abstract class AbstractTask implements Runnable {

  /** Reference to the shared list. */
  protected SharedList list;
  /**
   * Reference to object used to record data into the DB.
   * It may be {@code null} when we don't want to write to the DB.
   */
  protected IDBRegister dbregister = null;
  /** Number of operation repetitions. */
  protected int rep;
  /**
   * Id of the current test (the only purpose is to write it
   *                          along with other data do the DB).
   */
  protected int testId;

  /**
   * Initialize this instance properties..
   *
   * @param list Reference to the shared list.
   * @param dbregister Reference to object used to record data into the DB.
   * @param rep Number of operation repetitions.
   * @param testId Id of the current test.
   *
   * <p>
   * <b>Pre-conditions:</b>
   *  <ul>
   *    <li>list != null
   *    <li>rep > 0
   *  </ul>
   * </p>
   */
  public void initialize(SharedList list, IDBRegister dbregister, int rep, int testId) {
    assert list != null && rep > 0;

    this.list = list;
    this.dbregister = dbregister;
    this.rep = rep;
    this.testId = testId;
  }

  /**
   * Execute some operation {@rep} times on the shared list
   * and record the best access time.
   */
  public void run() {
    long t_start, t_db;

    t_start = System.nanoTime();
    for (int i = 0; i < rep; ++i) {
      try {
        execute();
      } catch (InterruptedException ex) {
        Logger.getLogger(AbstractTask.class.getName()).log(Level.SEVERE,
                "Error while executing task.", ex);
        System.exit(1);
      }
    }
    t_db = (System.nanoTime() - t_start) / rep;
    /** Add to the DB. */
    if (dbregister != null) {
      dbregister.addTestResult(new DBTestResult(this.testId, t_db));
    }
  }

  /**
   * Execute some operation on the shared list.
   */
  protected abstract void execute() throws InterruptedException;
}
